package com.my.framework.web.mvc.handler;

import java.lang.reflect.Method;
import java.util.Map;

import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod;

import com.my.exception.Message;
import com.my.framework.web.annotation.Page;
import com.my.model.Response;
import com.my.toolkit.JsonUtil;
import com.my.utils.LogUtil;
import com.my.utils.StringUtil;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

@Slf4j
//拦截BaseController的方法执行,处理异常和将返回值做封装成页面需要的格式
public class BaseControllerMethodHander extends ServletInvocableHandlerMethod{

	private JsonUtil jsonUtil;
	
	private HandlerMethod handlerMethod;
	
	public BaseControllerMethodHander(HandlerMethod handlerMethod,JsonUtil jsonUtil) {
		super(handlerMethod);
		this.handlerMethod=handlerMethod;
		this.jsonUtil=jsonUtil;
	}

	//将controler的返回值转为页面需要的形式
	protected Object doInvoke(Object... args) throws Exception {
		ReflectionUtils.makeAccessible(getBridgedMethod());
		try {
			Object o = getBridgedMethod().invoke(getBean(), args);
			return convertReturnValue(o, handlerMethod);
		} catch (Exception ex) {
			return error(ex,handlerMethod);
		}
	}
	
	//处理参数错误
	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		try {
			Object obj=super.invokeForRequest(request, mavContainer, providedArgs);
			return obj;
		}catch(Exception ex) {
			return error(ex,handlerMethod);
		}
		
	}
	
	
	@SneakyThrows
	public Object error(Exception ex,HandlerMethod handlerMethod) {
		Throwable cause=LogUtil.getCause(ex);
		if(ex instanceof MethodArgumentTypeMismatchException) {
			cause=new Message("参数输入不正确:"+cause.getMessage());
		}
		
		if(ex instanceof BindException) {
			BindException bind=(BindException)ex;
			StringBuilder sb=new StringBuilder("");
			for(ObjectError err:bind.getAllErrors()) {
				sb.append(toMessage(err.getDefaultMessage())+",");
			}
			cause=new Message(sb.substring(0,sb.length()-1));
		}
		
		if(!(cause instanceof Message)) {
			String message="执行controller方法出错:";
			message+=handlerMethod.getMethod().getDeclaringClass().getName();
			message+="."+handlerMethod.getMethod().getName();
			log.error(message+"["+cause.getMessage()+"]",ex);
			cause.printStackTrace();
		}
		
		if (AnnotationUtils.findAnnotation(handlerMethod.getMethod(), ResponseBody.class) != null) {
			return toResponseJson(cause);
		}
		
		if(cause instanceof Message) {
			ModelAndView view=new ModelAndView();
			view.setViewName("/error/error");
			view.addObject("message",cause.getMessage());
			return view;
		}
		
		throw ex;
	}
	
	//参数绑定异常被吃了,根据异常信息,自己翻译
	public String toMessage(String err) {
		if(StringUtil.isEmpty(err)) {
			return "";
		}
		String find=Message.class.getName();
		int idx=err.indexOf(find);
		if(idx!=-1) {
			return err.substring(idx+find.length()+1);
		}
		find="java.lang.NumberFormatException: For input string:";
		idx=err.indexOf(find);
		if(idx!=-1) {
			return "转为数字出错："+err.substring(idx+find.length()+1).replace("\"", "");
		}
		return err;
	}
	
	public Object convertReturnValue(Object o, HandlerMethod handlerMethod) {
		if (o instanceof ModelAndView) {
			//TODO 如果viewName以/开头,打成jar后会找不到模板...
			ModelAndView v=(ModelAndView)o;
			String name=v.getViewName();
			if(name.startsWith("/")) {
				v.setViewName(name.substring(1));
			}
			return o;
		}
		
		if(o instanceof String) {
			return o;
		}

		// 设置了ResponseBody,封装成Response
		if (AnnotationUtils.findAnnotation(handlerMethod.getMethod(), ResponseBody.class) != null) {
			return toResponseJson(o);
		}

		ModelAndView view = getDefaultView(handlerMethod);
		
		log.debug("返回页面:" + view.getViewName());
		if (o != null) {
			if(o instanceof Map) {
				view.addAllObjects((Map)o);
			}else {
				view.addObject(o);
			}
		}
		return view;
	}
	

	
	//所有json请求统一封装成Response
	private Response toResponseJson(Object o) {
		Response resp=null;
		if(o instanceof Response) {
			resp=(Response)o;
		}else if(o instanceof Throwable){
			Throwable error=(Throwable)o;
			resp=Response.fail(error.getMessage());
		}else {
			resp=Response.success(o);
		}

		log.debug("返回json{}",jsonUtil.toString(resp));
		return resp;
	}

	// 根据配置的requestMapping返回默认的template，规则为和requestMapping一样
	// 注：如果RequestMapping配了多个，只取第一个
	private ModelAndView  getDefaultView(HandlerMethod handlerMethod) {
		String name = "";
		Method method = handlerMethod.getMethod();
		RequestMapping mp = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), RequestMapping.class);
		if (mp != null) {
			name = mp.value()[0];
		}
		

		mp = AnnotationUtils.findAnnotation(method, RequestMapping.class);
		if (mp != null&&mp.value().length>0) {
			name += mp.value()[0];
		}else {
			Page page=AnnotationUtils.findAnnotation(method, Page.class);
			if(page!=null) {
				name+=page.path();
			}
		}
		
		
		
		if (name.startsWith("/")) {
			name= name.substring(1);
		}
		
		ModelAndView view=new ModelAndView();
		view.setViewName(name);
		return view;

	}
}
